﻿Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Threading

Public Class Form2
	Dim Keys As New List(Of String)
	Dim sProgress As String
	Dim sKombinaceKlaves As String
	Public Property lstVybranychStroke As List(Of InterceptionKeyStroke)
	Private ResetEvent As New AutoResetEvent(False)	' kvuli pockani na BackgroundWorker.CancelAsync(), protoze to chvili trva nez nastavi promennou, ktera se pak cte a jeji obsah neodpovida


	Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
		If dictCodeKey.Count = 0 Then
			InicializujSlovniky()
		End If

		Keys.AddRange(KeyCode.Key)
		Keys.AddRange(KeyCode.E0Key)
		Keys.AddRange(KeyCode.E1Key)
	End Sub

	Private Sub Form2_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
		lstVybranychStroke = Nothing
		lblKeyCombination.Text = "listening keyboard..."
		FlowPanel.Controls.Clear()
		TabControl1_SelectedIndexChanged(sender, e)
	End Sub

	Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

		Dim JizVybraneKeys As New List(Of String)
		For Each x As ComboBox In FlowPanel.Controls
			JizVybraneKeys.Add(x.SelectedItem)
		Next

		Dim PovoleneKeys As List(Of String) = Keys.Except(JizVybraneKeys).ToList

		Dim cmb As New ComboBox
		AddHandler cmb.SelectedIndexChanged, AddressOf ComboBox_SelectedIndexChanged
		cmb.DropDownStyle = ComboBoxStyle.DropDownList
		cmb.Items.AddRange(PovoleneKeys.ToArray)
		FlowPanel.Controls.Add(cmb)
	End Sub


	Private Sub ComboBox_SelectedIndexChanged(sender As Object, e As EventArgs)
		' nejprve se musi zrusit handle na tuhle funkci, jinak dochazi k nekonecne smycce
		For Each cmb As ComboBox In FlowPanel.Controls
			RemoveHandler cmb.SelectedIndexChanged, AddressOf ComboBox_SelectedIndexChanged
		Next

		Dim JizVybraneKeys As New List(Of String)
		For iCMB = 0 To FlowPanel.Controls.Count - 1
			Dim cmb As ComboBox = CType(FlowPanel.Controls(iCMB), ComboBox)
			JizVybraneKeys.Add(cmb.SelectedItem)
		Next

		For iCMB = 0 To FlowPanel.Controls.Count - 1
			Dim NepovoleneKeys As New List(Of String)
			For n = 0 To JizVybraneKeys.Count - 1
				If iCMB <> n Then
					NepovoleneKeys.Add(JizVybraneKeys(n))
				End If
			Next

			Dim cmb As ComboBox = CType(FlowPanel.Controls(iCMB), ComboBox)
			cmb.Items.Clear()
			cmb.Items.AddRange(Keys.Except(NepovoleneKeys).ToArray)
			cmb.SelectedItem = JizVybraneKeys(iCMB)
		Next

		' a znova nastavit event handler
		For Each cmb As ComboBox In FlowPanel.Controls
			AddHandler cmb.SelectedIndexChanged, AddressOf ComboBox_SelectedIndexChanged
		Next

	End Sub

	Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
		FlowPanel.Controls.Clear()
	End Sub

	Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
		FlowPanel.Controls.RemoveAt(FlowPanel.Controls.Count - 1)
		ComboBox_SelectedIndexChanged(sender, e)
	End Sub

	Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
		'MsgBox("BW spusteno.")
		Dim BW As BackgroundWorker = CType(sender, BackgroundWorker)
		KeyTest2(BW)
	End Sub

	Private Sub BackgroundWorker1_ProgressChanged2(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
		lblKeyCombination.Text = sKombinaceKlaves
	End Sub

	Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
		'MsgBox("BW ukonceno.")
	End Sub

	Private Sub KeyTest2(BW As BackgroundWorker)
		Dim hDll As IntPtr = LoadLibrary("interception.dll")
		Dim pointer_interception_is_keyboard As IntPtr = GetProcAddress(hDll, "interception_is_keyboard")
		Dim pointer_interception_is_mouse As IntPtr = GetProcAddress(hDll, "interception_is_mouse")

		Const ScanCode_ESC = &H1

		Dim context As IntPtr
		Dim device As System.Int32

		' vytvor context
		context = InterceptionWrapper.interception_create_context
		' nastav filtry
		Dim MyFilter As System.UInt16 = InterceptionFilterKeyState.INTERCEPTION_FILTER_KEY_DOWN Or InterceptionFilterKeyState.INTERCEPTION_FILTER_KEY_E0 Or InterceptionFilterKeyState.INTERCEPTION_FILTER_KEY_UP
		InterceptionWrapper.interception_set_filter(context, pointer_interception_is_keyboard, MyFilter)

		'''''''' alokovani UNmanaged promennych v pameti a vytvoreni pointeru na ne
		' vseobsazny STROKE, je dan velikosti mouse stroke, protoze je vetsi nez key stroke
		Dim AllStroke As InterceptionMouseStroke
		Dim pAllStroke As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(AllStroke))
		Marshal.StructureToPtr(AllStroke, pAllStroke, False)
		' mouse stroke
		Dim MouseStroke As InterceptionMouseStroke
		Dim pMouseStroke As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(MouseStroke))
		Marshal.StructureToPtr(MouseStroke, pMouseStroke, False)
		' key stroke
		Dim KeyStrokeProPamet, OrigKeyStroke As InterceptionKeyStroke
		Dim pKeyStroke As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(KeyStrokeProPamet))
		Marshal.StructureToPtr(KeyStrokeProPamet, pKeyStroke, False)

		Const HardwareIDbufferSize = 2000
		Dim pHardwareID As IntPtr = Marshal.AllocHGlobal(HardwareIDbufferSize)

		Dim lstStroke As New List(Of InterceptionKeyStroke)
		Dim lstPonechanychStroke As New List(Of InterceptionKeyStroke)
		Dim MoznoPridat As Boolean = True

		Try

			Do
				If BW.CancellationPending Then
					Exit Do
				End If

				' zarizeni na kterem byla akce
				device = InterceptionWrapper.interception_wait_with_timeout(context, 1)
				' co se zmacklo
				Dim receive As System.Int32 = InterceptionWrapper.interception_receive(context, device, pAllStroke, 1)
				If receive = 0 Then Continue Do

				' zkontroluj v Dictionary jestli je zarizeni sledovanym zarizenim

				' pokud je list uderu prazdnej, tak muzes do neho pridavat
				If lstStroke.Count = 0 Then
					MoznoPridat = True
				End If


				' zarizeni je klavesnice
				Dim SendResult As Integer
				If InterceptionWrapper.interception_is_keyboard(device) Then
					OrigKeyStroke = CType(Marshal.PtrToStructure(pAllStroke, GetType(InterceptionKeyStroke)), InterceptionKeyStroke)
					'If OrigKeyStroke.code = ScanCode_ESC Then Exit Do

					Dim KeyStroke As New InterceptionKeyStroke With {.code = OrigKeyStroke.code}

					Dim sState As String = ""
					If (OrigKeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_E0) = InterceptionKeyState.INTERCEPTION_KEY_E0 Then
						sState = sState & "E0"
						KeyStroke.state = KeyStroke.state Or InterceptionKeyState.INTERCEPTION_KEY_E0
					ElseIf (OrigKeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_E1) = InterceptionKeyState.INTERCEPTION_KEY_E1 Then
						sState = sState & "E1"
						KeyStroke.state = KeyStroke.state Or InterceptionKeyState.INTERCEPTION_KEY_E1
					End If
					If (OrigKeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_UP) = InterceptionKeyState.INTERCEPTION_KEY_UP Then
						sState = sState & "UP+"
						Dim ind As Integer = lstStroke.FindIndex(Function(x) x = KeyStroke)
						If ind <> -1 Then
							lstStroke.RemoveAt(ind)
							MoznoPridat = False
						End If
					ElseIf (OrigKeyStroke.state And InterceptionKeyState.INTERCEPTION_KEY_DOWN) = InterceptionKeyState.INTERCEPTION_KEY_DOWN Then
						sState = sState & "DOWN+"
						If MoznoPridat Then
							' najdi v seznamu aktualni stroke
							Dim ind As Integer = lstStroke.FindIndex(Function(x) x = KeyStroke)
							If ind = -1 Then ' pokud se nenasel aktualni stroke v seznamu, tak muze byt pridan
								lstStroke.Add(KeyStroke)
							End If
						End If
					End If


					'Marshal.StructureToPtr(OrigKeyStroke, pKeyStroke, False)
					'SendResult = InterceptionWrapper.interception_send(context, device, pKeyStroke, 1)



					Dim HardwareIDLength As System.Int32 = InterceptionWrapper.interception_get_hardware_id(context, device, pHardwareID, HardwareIDbufferSize)
					Dim sDevice As String = "none"
					If HardwareIDLength > 0 And HardwareIDLength < HardwareIDbufferSize Then
						sDevice = Marshal.PtrToStringAuto(pHardwareID)
					End If

					If MoznoPridat AndAlso lstStroke.Count <> 0 Then
						lstPonechanychStroke = lstStroke.ToList
						' uloz do globalni promenne jake zarizeni a co se zmacklo ...atd, a aktualizuj komponenty pres BW.ReportProgress(1)
						sProgress = sDevice & " : " & DejKey(OrigKeyStroke) & " : " & SendResult.ToString & " : " & sState
						If Interception.RightModifierKeysEqualsLeft Then
							lstPonechanychStroke = ZmenPraveModifierNaLeveAOdstranDuplicity(lstPonechanychStroke)
						End If
						If Interception.StartKeySequenceWithModifierKeys Then
							lstPonechanychStroke = UpravPoradiStrokuu(lstPonechanychStroke)
						End If
						sKombinaceKlaves = DejRetezecZeSeznamuStroku(lstPonechanychStroke)
						BW.ReportProgress(1)

					End If
				End If


			Loop
		Catch ex As Exception

		End Try

		InterceptionWrapper.interception_destroy_context(context)

		' uvolneni unmanaged promennych
		Marshal.FreeHGlobal(pAllStroke)
		Marshal.FreeHGlobal(pMouseStroke)
		Marshal.FreeHGlobal(pKeyStroke)

		FreeLibrary(hDll)

		lstVybranychStroke = lstPonechanychStroke.ToList	' nastav promennou s vybranymi stroky
		ResetEvent.Set()
	End Sub


	Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
		If TabControl1.SelectedTab.Name = "TabShortcut" Then
			PustSledovaniKlavesnice()
		ElseIf TabControl1.SelectedTab.Name = "TabSelection" Then
			ZastavSledovaniKlavesnice()
		End If

	End Sub

	Sub PustSledovaniKlavesnice()
		If Not BackgroundWorker1.IsBusy Then
			BackgroundWorker1.RunWorkerAsync()
		End If
	End Sub

	Sub ZastavSledovaniKlavesnice()
		If BackgroundWorker1.IsBusy Then
			BackgroundWorker1.CancelAsync()
			ResetEvent.WaitOne()
		End If
	End Sub

	Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
		ZastavSledovaniKlavesnice()
		If lstVybranychStroke.Count = 0 Then
			lstVybranychStroke = Nothing
		End If
		Me.Close()
	End Sub

	Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
		' pro kazdy ComboBox zjisti vybranou polozku, preved ji na Stroke a uloz
		lstVybranychStroke = New List(Of InterceptionKeyStroke)
		For iCMB = 0 To FlowPanel.Controls.Count - 1
			Dim cmb As ComboBox = CType(FlowPanel.Controls(iCMB), ComboBox)
			If cmb.SelectedItem IsNot Nothing Then
				lstVybranychStroke.Add(VytvorKeyStroke(cmb.SelectedItem))
			End If
		Next
		If lstVybranychStroke.Count = 0 Then
			lstVybranychStroke = Nothing
		Else
			lstVybranychStroke = UpravSeznamStrokuu(lstVybranychStroke)
		End If
		Me.Close()
	End Sub


	Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
		lstVybranychStroke = Nothing
		Me.Close()
	End Sub

	Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
		ZastavSledovaniKlavesnice()
		lstVybranychStroke = Nothing
		Me.Close()
	End Sub
End Class